﻿(function () {

    window.crucible = window.crucible || {};
    var crucible = window.crucible;

    

    crucible.ArgumentCanvas = function() {
        

        
    };

    crucible.ArgumentCanvas.prototype = function () {

        var argumentMargin = 100;
        var bottomRightMargin = 600;


            
        var init = function () {
            
            this.argumentContainer = $("#argumentContainer").addClass('argumentContainer');
            this.argumentDiv = $("#argumentDiv").addClass('argumentDiv');
            this.argumentCanvas = $("#argumentCanvas").addClass('argumentCanvas');
            this.nodeCache = {}; // dictionary of int, node
            this.rootNode = null;
            this.rowHeight = 0;
            this.colWidth = 0;
            
            $("#argumentResizeWrap").resizable({
                handles: 's',
                stop: function (event, ui) {
                    ui.element.css("width", "");
                    $("#argumentResizeWrap").css("height", $("#argumentContainer").css("height"));
                },
                resize: function (event, ui) {
                    ui.element.css("width", "");
                    $("#argumentContainer").css("height", ui.size.height);
                    $("#argumentResizeWrap").css("height", ui.size.height);
                }
            });

            setupPan(this.argumentContainer, this.argumentDiv);
        },
            

        draw = function () {
            var argumentCanvas = this.argumentCanvas,
                argumentContainer = this.argumentContainer,
                argumentDiv = this.argumentDiv,
                self = this;

            if (this.suppressDraw) return;

            var root = crucible.nodeManager.rootNode;

            

            var changed = positionTree(root, null, 0);
            
            changed = destroyFloatingElements(root) || changed;

            changed = createElements(this, root) || changed;

            if (changed) {

                clearCanvas(argumentCanvas);
                argumentCanvas.css("opacity", 0);

                animateElements(this, root, function() {

                    var dimensions = calculateDimensions(argumentDiv);

                    argumentDiv
                        .height(dimensions.height + bottomRightMargin)
                        .width(dimensions.width + bottomRightMargin);


                    argumentCanvas.attr('width', dimensions.width);
                    argumentCanvas.attr('height', dimensions.height);

                    drawCanvas(root, argumentCanvas);

                    argumentCanvas.animate({ opacity: 1 }, 100);
                    
                    if (Modernizr.touch && !self.myScroll) {
                        argumentContainer.css("overflow", "auto");
                        self.myScroll = new iScroll("argumentContainer");
                    }

                });
            }

        };


        //function fixupBody(self, node) {
        //    var finder = /\{\{\[([0-9]+)\]([^}]+)\}\}/g;

        //    var fixed = node.Body.replace(finder, '<a href="#" class="assertionLink" data-id="$1">$2</a>');

        //    var p = $('<p>' + fixed + '</p>');

        //    p.find("a").each(function (i, el) {
        //        var id = $(el).data("id");
        //        $(el).click(function () {
        //            gotoNode(self, id);
        //        });
        //    });

        //    return p;
        //}
        
        //function gotoNode(self, id) {
        //    var node = self.nodeCache[id];
        //    crucible.datacontext.focusNode(node);

        //}

        function calculateDimensions(argumentDiv) {

            var dimensions = { width: 0, height: 0 };
            // Loop through elements children to find & set the biggest height
            argumentDiv.find('.assertionBox').each(function () {
                var el = $(this);
                var bottom = el.position().top + el.outerHeight();
                var right = el.position().left + el.outerWidth();
                if (bottom > dimensions.height) {
                    dimensions.height = bottom;
                }
                if (right > dimensions.width) {
                    dimensions.width = right;
                }
            });

            return dimensions;

        }
        

        function createElements(self, node) {

            var id = 'node' + node.Id;
            if (node.cloneOf) {
                id += 'c' + node.cloneNum;
            }
           
            node.elementId = id;

            var changed = crucible.treeMakerViewModel.addIfNew(node);
            
            node.setEl();
            
            if (changed && node.parent) {
                node.el.css('top', node.parent.el.css('top'));
                node.el.css('left', node.parent.el.css('left'));
            }


            if (!self.rowHeight && node.colspan() === 1 && node.rowspan() === 1) {
                self.rowHeight = node.el.outerHeight(true);
                self.colWidth = node.el.outerWidth(true);
            }
            
            var i;

            if (!node.cloneOf) {

                if (node.IsFertile()) { // weak equals because the radio buttons set it to a string.
                    //node.HasChildren(node.Children().length); // true if > 0
                    for (i = 0; i < node.Children().length; i++) {
                        changed = createElements(self, node.Children()[i]) || changed;
                    }
                } else {
                    changed = destroyChildElements(node) || changed;
                }
            }

            return changed;
        }
        
        function animateElements(self, node, animationComplete) {
            
            node.el.animate({
                top: node.y * self.rowHeight + argumentMargin,
                left: node.x * self.colWidth + argumentMargin
            },
            {
                duration: 200,
                queue: false,
                complete: function () {
                    if (animationComplete) {
                        setTimeout(function () {
                            animationComplete();
                        }, 5);
                    }
                }
            });

            var i;

            if (!node.cloneOf) {

                if (node.IsFertile()) {
                    for (i = 0; i < node.Children().length; i++) {
                        animateElements(self, node.Children()[i]);
                    }
                }
            }
        }
        
        function destroyChildElements(node) {
            var i, changed = false;
            //if (!node.Children().length) {
            //    return false;
            //}
            for (i = 0; i < node.Children().length; i++) {
                var child = node.Children()[i];
                changed = crucible.treeMakerViewModel.removeNode(child).length || changed;
                changed = destroyChildElements(child) || changed;
                child.el = null;
            }

            //node.HasChildren(false);

            return changed;
        }
        
        function destroyFloatingElements(rootNode) {
            var attachedNodes = listAttachedNodes(rootNode);

            return  crucible.treeMakerViewModel.removeFloaters(attachedNodes);
        }
        
        function listAttachedNodes(node) {
            var list = [];
            if (node) {
                list.push(node);
                for (var i = 0; i < node.Children().length; i++) {
                    list = list.concat(listAttachedNodes(node.Children()[i]));
                }
            }
            return list;
        }

        //function scrollTo(self, node) {

        //    self.argumentContainer.animate({
        //        scrollTop: node.position().top - 200,
        //        scrollLeft: node.position().left - 200
        //    }, 300);
        //}


        function clearCanvas(canvas) {
            var canvasEl = canvas[0] || canvas,
                context = canvasEl.getContext("2d");
            context.clearRect(0, 0, canvasEl.width, canvasEl.height);
        }

        function drawCanvas(root, argumentCanvas) {
            var canvasEl = argumentCanvas[0] || argumentCanvas,
                context = canvasEl.getContext("2d");

            drawConnectors(root, context);
        }

        function drawConnectors(root, context) {
            var i;
            if (root.Children && root.el) {
                for (i = 0; i < root.Children().length; i++) {
                    drawConnector(root, root.Children()[i], context);
                    drawConnectors(root.Children()[i], context);
                }
            }
        }

        function drawConnector(parent, child, context) {

            if (!child.el) {
                return;
            }

            var start = {
                x: parent.el.position().left + parent.el.width(),
                y: parent.el.position().top + parent.el.height() / 2
            };

            var end = {
                x: child.el.position().left,
                y: child.el.position().top + child.el.height() / 2
            };

            var middle = (start.x + end.x) / 2;

            context.beginPath();
            context.moveTo(start.x, start.y + 3);
            context.bezierCurveTo(middle, start.y + 3, middle, end.y + 3, end.x, end.y + 3);
            context.lineWidth = 6;
            context.strokeStyle = "black";
            context.stroke();

            context.beginPath();
            context.moveTo(start.x, start.y);
            context.bezierCurveTo(middle, start.y, middle, end.y, end.x, end.y);
            context.lineWidth = 6;
            context.strokeStyle = "#7AC1E2";
            context.stroke();


        }

        function setupPan(container, draggable) {
            
            var mouseDown = false;
            var mouseX = 0;
            var mouseY = 0;
            var originalLeft = container.scrollLeft();
            var originalTop = container.scrollTop();


            function startDrag(event) {
                mouseDown = true;
                mouseX = event.pageX;
                mouseY = event.pageY;
                originalLeft = container.scrollLeft();
                originalTop = container.scrollTop();
                $(document).disableSelection();
                return true;
            }

            function stopDrag(event) {
                mouseDown = false;
                $(document).enableSelection();
                return true;
            }
            
            function dragMove(event) {
                if (mouseDown === true) {
                    var panLeft = (event.pageX - mouseX);
                    var panTop = (event.pageY - mouseY);

                    if (panLeft !== 0 || panTop !== 0) {
                        container.scrollLeft(originalLeft - panLeft);
                        container.scrollTop(originalTop - panTop);
                    }
                }

                return true;
            }

            draggable.mousedown(startDrag);
            
            $(document).mouseup(stopDrag);
            $(document).mousemove(dragMove);
            
        }

        function maxInRange(start, length, list) {
            var i,
                max = 0;

            for (i = start; i < start + length && i < list.length; i++) {
                max = Math.max(max, list[i]);
            }

            return max;
        }

        function smearValue(start, length, list, value) {
            var i;
            for (i = start; i < start + length; i++) {
                list[i] = value;
            }
        }
        

        function positionTree(node, peakList, cursorX) {
            var oldx = node.x,
                oldy = node.y,
                changed = false;
            
            delete node.x;
            delete node.y;

            console.log("positioning node " + node.Id);

            if (!peakList) {
                peakList = [];
                node.x = cursorX;
                node.y = 0;
                peakList[cursorX] = 0;
            }

            if (node.cloneOf || node.Children().length === 0) {
                // This is a leaf, so we can position its unpositioned ancestors
                var cy;
                var minDescent = 0;
                var current = node;
                var cx = cursorX;

                while (current && current.y === undefined) {
                    current.x = cx;
                    
                    if (peakList[cx] !== undefined) {
                        if (current.colspan() > 1) {
                            cy = maxInRange(cx, current.colspan(), peakList);
                        } else {
                            cy = peakList[cx];
                        }
                        var desc = 1;
                        //if (current.parent.Children()[0] === current) {
                        //    desc = 2;
                        //}
                        minDescent = Math.max(cy + desc, minDescent);
                    }
                    current = current.parent;
                    if (current) {
                        cx -= current.colspan();
                    }
                }

                current = node;
                cx = cursorX;
                while (current && current.y === undefined) {
                    current.y = minDescent;
                    if (current.colspan() > 1) {
                        smearValue(cx, current.colspan(), peakList, minDescent + current.rowspan() - 1);
                    } else {
                        peakList[cx] = minDescent + current.rowspan() - 1;
                    }
                    current = current.parent;
                    if (current) {
                        cx -= current.colspan();
                    }
                }

            } else {

                var c;
                for (c = 0; c < node.Children().length; c++) {
                    changed = positionTree(node.Children()[c], peakList, cursorX + node.colspan()) || changed;
                }
            }

            return changed || node.x !== oldx || node.y !== oldy;

        }

        return {
            init: init,
            draw: draw
        };

    }();


}());